using System;
using System.Linq.Expressions;

namespace K3Cloud.WebApi.Core.IoC.Extensions
{
    /// <summary>
    /// 常量折叠优化规则
    /// 将可在编译时计算的常量表达式提前计算
    /// 例如: x => x.Price > 100 * 1.2 优化为 x => x.Price > 120
    /// </summary>
    public class ConstantFoldingRule : IOptimizationRule
    {
        public string Name => "常量折叠";

        public int Priority => 1;

        public bool CanApply(Expression expression)
        {
            // 检查是否包含可折叠的常量运算
            var checker = new ConstantFoldingChecker();
            checker.Visit(expression);
            return checker.HasFoldableConstants;
        }

        public Expression Apply(Expression expression)
        {
            return new ConstantFoldingVisitor().Visit(expression);
        }

        /// <summary>
        /// 检查是否有可折叠的常量
        /// </summary>
        private class ConstantFoldingChecker : ExpressionVisitor
        {
            public bool HasFoldableConstants { get; private set; }

            protected override Expression VisitBinary(BinaryExpression node)
            {
                if (node.Left is ConstantExpression && node.Right is ConstantExpression)
                {
                    HasFoldableConstants = true;
                }
                return base.VisitBinary(node);
            }
        }

        /// <summary>
        /// 常量折叠访问器
        /// </summary>
        private class ConstantFoldingVisitor : ExpressionVisitor
        {
            protected override Expression VisitBinary(BinaryExpression node)
            {
                var left = Visit(node.Left);
                var right = Visit(node.Right);

                // 如果左右都是常量，尝试在编译时计算
                if (left is ConstantExpression leftConst && right is ConstantExpression rightConst)
                {
                    try
                    {
                        object? result = null;

                        // 处理算术运算
                        switch (node.NodeType)
                        {
                            case ExpressionType.Add:
                            case ExpressionType.AddChecked:
                                result = AddValues(leftConst.Value, rightConst.Value);
                                break;
                            case ExpressionType.Subtract:
                            case ExpressionType.SubtractChecked:
                                result = SubtractValues(leftConst.Value, rightConst.Value);
                                break;
                            case ExpressionType.Multiply:
                            case ExpressionType.MultiplyChecked:
                                result = MultiplyValues(leftConst.Value, rightConst.Value);
                                break;
                            case ExpressionType.Divide:
                                result = DivideValues(leftConst.Value, rightConst.Value);
                                break;
                            case ExpressionType.Modulo:
                                result = ModuloValues(leftConst.Value, rightConst.Value);
                                break;
                        }

                        if (result != null)
                        {
                            return Expression.Constant(result, node.Type);
                        }
                    }
                    catch
                    {
                        // 如果计算失败，保持原样
                    }
                }

                if (left != node.Left || right != node.Right)
                {
                    return Expression.MakeBinary(node.NodeType, left, right, node.IsLiftedToNull, node.Method);
                }

                return node;
            }

            private static object? AddValues(object? left, object? right)
            {
                if (left == null || right == null) return null;

                return left switch
                {
                    int l when right is int r => l + r,
                    long l when right is long r => l + r,
                    decimal l when right is decimal r => l + r,
                    double l when right is double r => l + r,
                    float l when right is float r => l + r,
                    short l when right is short r => (short)(l + r),
                    byte l when right is byte r => (byte)(l + r),
                    _ => null
                };
            }

            private static object? SubtractValues(object? left, object? right)
            {
                if (left == null || right == null) return null;

                return left switch
                {
                    int l when right is int r => l - r,
                    long l when right is long r => l - r,
                    decimal l when right is decimal r => l - r,
                    double l when right is double r => l - r,
                    float l when right is float r => l - r,
                    short l when right is short r => (short)(l - r),
                    byte l when right is byte r => (byte)(l - r),
                    _ => null
                };
            }

            private static object? MultiplyValues(object? left, object? right)
            {
                if (left == null || right == null) return null;

                return left switch
                {
                    int l when right is int r => l * r,
                    long l when right is long r => l * r,
                    decimal l when right is decimal r => l * r,
                    double l when right is double r => l * r,
                    float l when right is float r => l * r,
                    short l when right is short r => (short)(l * r),
                    byte l when right is byte r => (byte)(l * r),
                    _ => null
                };
            }

            private static object? DivideValues(object? left, object? right)
            {
                if (left == null || right == null) return null;

                return left switch
                {
                    int l when right is int r && r != 0 => l / r,
                    long l when right is long r && r != 0 => l / r,
                    decimal l when right is decimal r && r != 0 => l / r,
                    double l when right is double r && r != 0 => l / r,
                    float l when right is float r && r != 0 => l / r,
                    _ => null
                };
            }

            private static object? ModuloValues(object? left, object? right)
            {
                if (left == null || right == null) return null;

                return left switch
                {
                    int l when right is int r && r != 0 => l % r,
                    long l when right is long r && r != 0 => l % r,
                    decimal l when right is decimal r && r != 0 => l % r,
                    double l when right is double r && r != 0 => l % r,
                    float l when right is float r && r != 0 => l % r,
                    _ => null
                };
            }
        }
    }
}
